#ifndef CXXUINT_H
#define CXXUNIT_H

/**\ a simple unit test implementation.
* 
*For a DEV, it's a nightmare debugging code step by step one day after another.
*Unit-Test is the TOP important thing for him/her to do.
*and this is very simple but convenient unit test framework(it only has a header file).
* 
*/



#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#include <sstream>
#ifdef WIN32
	#include <windows.h>
#endif


typedef std::string tstring;



///test case interface.
class ITest
{	
public:
	virtual ~ITest()
	{}
	
	/** run the test: run the code and check result*/
	virtual void Run() = 0;
	
	/** Get the test case info */
	virtual tstring	GetName() const = 0;
};


///Listen category 
enum ListenType
{
	LDEBUG	= 0x0000001,
	LLOG	= 0x0000010,
	LSUCESS= 0x0000100,
	LWARNING= 0x0001000,
	LERROR	= 0x0010000,	
	LALL	= 0x1111111,
};


///Listener interface
class IListener
{
public:
	virtual ~IListener()
	{}

	virtual bool BeginListen()
	{
		return true;
	}	
	
	virtual void Listen(ListenType type,const tstring& msg) = 0;
	
	virtual void EndListen()
	{
	}

	virtual bool SetConfig(void* param)
	{
		return true;
	}
protected:	
private:
};





///Concrete Console listener.
class ConsoleListener : public IListener
{
private:
#ifdef WIN32
	enum TEXT_COLOR
	{
		TEXT_BLUE	=	FOREGROUND_BLUE,
		TEXT_GREEN	=	FOREGROUND_GREEN,
		TEXT_RED	=	FOREGROUND_RED,
		TEXT_YELLOW	=	FOREGROUND_GREEN | FOREGROUND_RED,
		TEXT_NORMAL	=	FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ,//white
		TEXT_HIGHT	=	FOREGROUND_INTENSITY
	};
	
	void SetTextStyle(TEXT_COLOR value, bool bHight)
	{
		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),value | (bHight ? FOREGROUND_INTENSITY : 0)); 
	}	
#else
	#define TEXT_BLUE	"\033[36;1m"
	#define TEXT_GREEN	"\033[32;1m"
	#define TEXT_RED	"\033[31;1m"
	#define TEXT_YELLOW "\033[93;1m"
	#define TEXT_NORMAL "\033[0m"
	#define TEXT_HIGHT	"\033[" "1m"
	void SetTextStyle(const TCHAR* value, bool bHight)
	{
		printf("%s%s",value, bHight ? TEXT_HIGHT:"");
	}
#endif
	#define RestoreTextStyle() SetTextStyle(TEXT_NORMAL,false)

public:
	static IListener& Instance()
	{
		static ConsoleListener instance;
		return instance;
	}

	virtual void Listen(ListenType type,const tstring& value)
	{
		switch (type)
		{
		case LERROR:
			SetTextStyle(TEXT_RED,true);
			break;
		case LWARNING:
			SetTextStyle(TEXT_YELLOW,true);
			break;
		case LDEBUG:
			SetTextStyle(TEXT_BLUE,true);
			break;
		case LSUCESS:
			SetTextStyle(TEXT_GREEN,true);
			break;
		case LLOG:
			SetTextStyle(TEXT_BLUE,true);
			break;
		}
		std::cout<<value<<std::endl;

		RestoreTextStyle();
	}
};



///Concrete Console listener.
class CIDEListener : public IListener
{
private:


public:	
	static IListener& Instance()
	{
		static CIDEListener instance;
		return instance;
	}

	virtual void Listen(ListenType type,const tstring& value)
	{

	}
};


/**\assert value whether expected.
* 
* import full of type-trait technology isn't necessary.
*/
class CAssert
{

public:	
	static IListener* Listener(IListener* pListener=NULL)
	{
		static IListener* thelistener = &ConsoleListener::Instance();
		if (pListener)
		{
			thelistener = pListener;
		}
		return thelistener;
	}
	
	

	
	
	//in most of cases, T is a value type,not a reference type.
	template<typename T>
	static bool assertEquals(const T expected, const T result,const TCHAR* file, int line,const TCHAR* msg)
	{
		if (expected != result)
		{			
			std::stringstream interpreter;			
			interpreter<<"AssertFailed at:"<<file<<" line:"<<line<<std::endl<<msg<<std::endl;
			interpreter << "expected:"<<expected<<std::endl<<"result is:"<<result;			
			
			Listener()->Listen(LERROR,interpreter.str());
			return false;
		}
		return true;
	}	
	
	///Type trait
	static  bool assertEquals( const TCHAR * expected, const TCHAR * result,const TCHAR* file, int line,const TCHAR* msg)
	{
		return assertEquals(tstring(expected), tstring(result),file, line,msg);
	}
	
	static bool assertEquals(double expected, double result, const TCHAR* file, int line,const TCHAR* msg)
	{
		const double fuzzyEpsilon = 0.000001;
		bool iszero= fabs(expected - result) < fuzzyEpsilon;
		bool bresult = true;	
		return assertEquals(iszero,bresult,file,line,msg);
	}
	
private:	
	CAssert();	
};


///this is the main implementation of CXXUNIT
template<class ConcreteTestSuit>
class CTestSuit : public ITest
{

//////////////////////////////////////////////////////////
protected:
	typedef void(ConcreteTestSuit::*TestCaseMethod)();
	class TestCase : public ITest
	{
	public:
		TestCase(ConcreteTestSuit* parent, TestCaseMethod method, const tstring & name):
			m_parent(parent), m_testCaseMethod(method), m_name(name)
			{
			}

		void Run()
		{
			(m_parent->*m_testCaseMethod)();
		}

		tstring GetName() const
		{
			return m_name;
		}
	private:
		ConcreteTestSuit* m_parent;
		TestCaseMethod m_testCaseMethod;
		tstring m_name;
	};




//////////////////////////////////////////////////////////
public:
	void RegistTestCase(ConcreteTestSuit* parent, TestCaseMethod method, const TCHAR* name)	
	{
	
	
		TestCase* casDeTest = new TestCase(parent, method,m_name + "::" + name);
		m_testCases.push_back( casDeTest );
	}
	
	


	typedef std::vector<ITest*> CaseContainer;
	
	CTestSuit(const tstring& name ,IListener* pListener) :m_name(name), m_plistener(pListener)
	{
		if (m_plistener == NULL)
		{
			m_plistener = &ConsoleListener::Instance();
		}
	}
	virtual bool BeginTest()
	{
		std::cout<<"BeginTest"<<std::endl;
		return true;
	}
	virtual void EndTest()
	{		
		std::cout<<"EndTest"<<std::endl;
	}
	void Run()
	{
		
		if ( BeginTest() )
		{
			CaseContainer::iterator it;
			tstring info;
			for (it =m_testCases.begin(); it!=m_testCases.end(); ++it)
			{
				try
				{
					m_plistener->Listen(LLOG,"{{ " + (*it)->GetName());
					(*it)->Run();
					m_plistener->Listen(LLOG,"}}" );

				}
				catch (std::exception& except )
				{
					info = except.what();
					m_plistener->Listen(LERROR,info);
				}
				catch (...)
				{
					info = tstring("unknow exception at") + (*it)->GetName();
				}
			}
		}
		
		EndTest();
	}
	virtual ~CTestSuit()
	{
		for (CaseContainer::iterator it =m_testCases.begin(); it!=m_testCases.end(); ++it)
		{
			delete (*it);
		}
	}
	tstring GetName() const
	{
		return m_name;
	}
private:
	CaseContainer m_testCases;
	tstring m_name;
	IListener * m_plistener;

};






class CTestSuitsFactory
{
	
public:
	static CTestSuitsFactory& Instance()
	{
		static CTestSuitsFactory instance;
		return instance;
	}
	
	/**
		the memory created by it->m_creator will be auto deleted by std::auto_ptr, so there is no memory leak!
	*/
	bool Run()
	{
		std::sort(m_creatorinfos.begin(),m_creatorinfos.end());
		
		for (std::vector<TestSuitCreatorInfo>::iterator it=m_creatorinfos.begin(); it!=m_creatorinfos.end(); ++it)
		{
			std::auto_ptr<ITest> test(it->m_creator());
			test->Run();
		}
		return true;
	}
	
	
	typedef ITest* (*TestSuitCreator)();
	
	void AddSuitCreator(TestSuitCreator creator,unsigned sequence)
	{
		TestSuitCreatorInfo info(creator,sequence);
		m_creatorinfos.push_back( info );
	}


	/**
	by default, the test cases' running sequence are depended on the compiler compile files' sequence,
	so  we add m_sequence which would control the test case running  order.
	*/
private:
	struct TestSuitCreatorInfo
	{
		unsigned m_sequence;
		TestSuitCreator m_creator;
		TestSuitCreatorInfo(TestSuitCreator Creator,unsigned Sequence):m_creator(Creator),m_sequence(Sequence)	
		{
		}
		bool operator < (const TestSuitCreatorInfo& right)
		{
			return m_sequence < right.m_sequence;
		}
	};	
	std::vector<TestSuitCreatorInfo> m_creatorinfos;
private:
	CTestSuitsFactory()
	{
	}
	CTestSuitsFactory& operator = (const CTestSuitsFactory& right);
};



#define TEST_BEGIN(theConcreteTestSuit) TEST_BEGIN_WITHLISTENER(theConcreteTestSuit,NULL)

#define TEST_BEGIN_WITHLISTENER(theConcreteTestSuit,listener ) \
	theConcreteTestSuit() : CTestSuit<theConcreteTestSuit> (#theConcreteTestSuit,listener) {

#define TEST_CASE(theConcreteTestSuit, methodName ) \
	RegistTestCase( this,& theConcreteTestSuit::methodName, #methodName );

#define  TEST_END()	}





//sometime we need cases run with special m_sequence
#define REGISTER_TESTSUIT_BYSEQ( theConcreteTestSuit ,m_sequence) \
	ITest* Create##theConcreteTestSuit() { return new theConcreteTestSuit; } \
	class Register_##theConcreteTestSuit { \
	public: \
	Register_##theConcreteTestSuit() 	{ \
		CTestSuitsFactory::Instance().AddSuitCreator( \
			Create##theConcreteTestSuit,m_sequence); \
		} \
	}; \
	static Register_##theConcreteTestSuit estatic_##theConcreteTestSuit;


#define REGISTER_TESTSUIT( theConcreteTestSuit ) REGISTER_TESTSUIT_BYSEQ( theConcreteTestSuit, 0xffffffff )





/**
* Assert macros to use in test methods. An assert is a test condition
* we want to check.
*/
#define _ASSERT_(exp) CAssert::assertEquals(exp, true, __FILE__, __LINE__,#exp)
#define _ASSERT_EQUALS_(result, expected)	CAssert::assertEquals( expected, result, __FILE__, __LINE__ ,"")





#ifdef CXX_TEST_1
class mytest : public CTestSuit<mytest>
{
public:
	TEST_BEGIN(mytest,NULL)
		TEST_CASE(func)
	TEST_END()

	void func()
	{
		_ASSERT_(1==2);
	}

protected:
private:
};
REGISTER_TESTSUIT(mytest)



void run_test()
{
	ACE::init();
	CTestSuitsFactory::Instance().Run();
	ACE::fini();
}


#endif // CXX_TEST

#endif